#include <string>
#include <vector>
#include <iostream>
using namespace std;
#include "sys/process.h"
#include "utils/cmd_arg.h"
#include "mem/small_buffer.h"
#include "component/logger.h"
using namespace tdl;
#include "ccs.h"
#include "btypes.h"
using namespace tcsf;

namespace tcsf {
// specialization of template function must be put before instantiation in the initialize()
template <>
void ccs::__buss_thread_main<ccs::udp_channel>(ccs::udp_channel* pcn)
{
	while(1)
	{
		size_t rl;
		nreq_type req;

		pcn->req_q.pop(req, rl);
	m_access_logger.info() << req << rl << endl;	
		pcn->req_so.call_sofunc<TCSF_SOF_ID_DOREQ>(&req, &m_access_logger);
	}
}

template <>
void ccs::__buss_thread_main<ccs::tcp_channel>(ccs::tcp_channel* pcn)
{
}

////////////////
ccs::ccs()
{
	m_daemon = false;
}

ccs::~ccs()
{
}

void ccs::show_usage()
{
	std::cout << "usage: ./ccs [--help] [--daemon] [--config=path/file.conf]" << endl;
}

void ccs::_init_args(int argc, char* argv[])
{
	if(argc < 2)
	{
		show_usage();
		terminate(0);
	}

	std::vector<tdl::cmd_arg> vca;
	vca.push_back(cmd_arg("help", false, 'h'));
	vca.push_back(cmd_arg("daemon", false, 'd'));
	vca.push_back(cmd_arg("config-file", true, 'c'));
	std::map<char, std::string> mopt = tdl::parse_cmd_args(vca, argc, argv);

	if(mopt.size() == 0)
	{
		cerr << "passing none or unknown arguments." << endl;
		show_usage();
		terminate(0);
	}

	std::map<char, std::string>::iterator opit;
	for(opit = mopt.begin(); opit != mopt.end(); ++opit)
	{
		switch(opit->first)
		{
		case 'h':
			show_usage();
			terminate(0);
		case 'd':
			m_daemon = true;
			break;
		case 'c':
			m_conf_file = opit->second;
			break;
		default:
			show_usage();
			terminate(0);
		}
	}
}

void ccs::_init_logger()
{
	tdl::logger_init_info lii;

	lii.log_file_name = m_conf["log\\sys_log\\path"];
	lii.max_logfile_size = tdl::s2u(m_conf["log\\sys_log\\max_logfile_size"]);
	lii.write_block_size = tdl::s2u(m_conf["log\\sys_log\\write_block_size"]);
	m_sys_logger.initialize(&lii, &m_clock, tdl::LOG_DEBUG, '|', true);

	lii.log_file_name = m_conf["log\\access_log\\path"];
	lii.write_block_size = tdl::s2u(m_conf["log\\access_log\\write_block_size"]);
	m_access_logger.initialize(&lii, &m_clock, tdl::LOG_DEBUG, '|', true);
}

void ccs::_init_network()
{
	try {
#ifdef SYS_EPOLL_H
		m_epoll.create(1024);
#endif

		std::vector<std::string> vs;
		std::vector<std::string> interface_ids = m_conf.get_subpath("interface");
		for(std::vector<std::string>::iterator it = interface_ids.begin();it != interface_ids.end(); ++it)
		{
			vs.clear();
			std::string path = "interface\\" + *it;

			std::string listen_addr = m_conf[path + "\\listen"];
			std::string so = m_conf[path + "\\so"];
			uint32_t buss_thread_num = tdl::s2u(m_conf[path + "\\thread"]);
			size_t req_qsize = tdl::s2u(m_conf[path + "\\req_qsize"]);

			std::string tmp_ha(listen_addr.data());
			tdl::split_string(tmp_ha, "@", vs);

			tdl::host_addr ha = vs[1];

			tdl::socket* psock;

			if(vs[0] == "udp")
			{
				tdl::udp_socket s = tdl::udp_socket::create();
				s.bind(ha);

				udp_channel *pcn = new udp_channel(s);

				pcn->build_req_queue(req_qsize);

				pcn->start_business_thread_pool(make_memfun_exec(*this, &ccs::__buss_thread_main<ccs::udp_channel>, pcn), buss_thread_num, so);

				m_sys_logger.info(tdl::LOG_ORG_FMT) << "creating udp socket on " << ha << endl;

				m_udp_channels.insert(make_pair(s.get_fd(), pcn));

				psock = &s;
			}
			else if(vs[0] == "tcp")
			{
				tdl::passive_tcp_socket s = tdl::passive_tcp_socket::create(ha);
	
				tcp_channel *pcn = new tcp_channel(s);

				pcn->build_req_queue(req_qsize);

				pcn->start_business_thread_pool(make_memfun_exec(*this, &ccs::__buss_thread_main<ccs::tcp_channel>, pcn), buss_thread_num, so);

				m_sys_logger.info(tdl::LOG_ORG_FMT) << "creating tcp socket on " << ha << endl;

				m_tcp_channels.insert(make_pair(s.get_fd(), pcn));

				psock = &s;
			}
			else
			{
				cerr << "unknown protocol: " << listen_addr << endl;
				terminate(1);
			}

#ifdef SYS_EPOLL_H
			m_epoll.add_fd(psock->get_fd(), EPOLLIN | EPOLLET);
#endif
		}
	} catch(socket_ex& e) {
		cerr << e.what_hell() << endl;
		terminate(1);
#ifdef SYS_EPOLL_H
	} catch(epoll_ex& e) {
		cerr << e.what_hell() << endl;
#endif
	} catch(soload_ex& e) {
		cerr << e.what_hell() << endl;
		terminate(1);
	}
}

void ccs::initialize(int argc, char* argv[])
{
	_init_args(argc, argv);

	if(m_daemon)
		tdl::daemon();

	m_clock.initialize(10);

	m_conf.initialize(m_conf_file);

	_init_logger();

	_init_network();
}

/////////////////////////
#ifdef SYS_EPOLL_H
void ccs::__handle_network_epoll__()
{
	try {
		epoll::result er = m_epoll.wait();
	} catch(tdl::epoll_ex& e) {
		m_sys_logger.error() << e.what() << endl;
		return;
	}
		
	for(uint32_t i = 0; i < er.second; ++i)
	{
		epoll_event& ev = er.first[i];

		int fd = ev.data.fd;

		// check udp channels
		udp_channel_type::iterator udp_it = m_udp_channels.find(fd);
		if(udp_it != m_udp_channels.end())
		{
			__handle_udp__(udp_it->second);
			continue;
		}

		// check tcp channels
		tcp_channel_type::iterator tcp_it = m_tcp_channels.find(fd);
		if(tcp_it != m_tcp_channels.end())
		{
			__handle_tcp__(tcp_it->second);
			continue;
		}
	}
}
#else
void ccs::__handle_network_select__()
{
	fd_set rfds;
	int max_fd = 0;
	FD_ZERO(&rfds);

	udp_channel_type::iterator udp_it = m_udp_channels.begin();
	for(; udp_it != m_udp_channels.end(); ++udp_it)
	{
		int fd = udp_it->first;

		FD_SET(fd, &rfds);
		max_fd = max(max_fd, fd);
	}

	tcp_channel_type::iterator tcp_it = m_tcp_channels.begin();
	for(; tcp_it != m_tcp_channels.end(); ++tcp_it)
	{
		int fd = tcp_it->first;

		FD_SET(fd, &rfds);
		max_fd = max(max_fd, fd);
	}

	int n = ::select(max_fd + 1, &rfds, NULL, NULL, NULL);
	if(n <= 0)
	{
		m_sys_logger.error() << "select() error: " << strerror(errno) << endl;
		return;
	}

	// check for udp channels
	udp_it = m_udp_channels.begin();
	for(; udp_it != m_udp_channels.end(); ++udp_it)
	{
		int fd = udp_it->first;
		if(FD_ISSET(fd, &rfds))
			__handle_udp__(udp_it->second);
	}

	// check for tcp channels
	tcp_it = m_tcp_channels.begin();
	for(; tcp_it != m_tcp_channels.end(); ++tcp_it)
	{
		int fd = tcp_it->first;
		if(FD_ISSET(fd, &rfds))
			__handle_tcp__(tcp_it->second);
	}
}
#endif

void ccs::__handle_udp__(ccs::udp_channel* pcn)
{
	tdl_assert(pcn);

	while(1)
	{
		ssize_t n = 0;
		nreq_type req;

		req.local_sock = pcn->lsocket;

		try {
			n = pcn->lsocket.recvfrom(req.data.ptr, req.data.size, req.peer_ha);
			if(n == 0 || n == -1 * EAGAIN)
				return;
		} catch(tdl::socket_ex& e) {
			m_sys_logger.error() << e.what_hell() << endl;
			continue;
		}
	
		req.data.len = n;

		pcn->req_q.push(req, req.length());
	}
}

void ccs::__handle_tcp__(ccs::tcp_channel* pcn)
{
}


/////////////////////
void ccs::run()
{
	m_sys_logger.info(tdl::LOG_ORG_FMT) << "waiting..." << endl;

	while(1)
	{
#ifdef SYS_EPOLL_H
		__handle_network_epoll__();
#else
		__handle_network_select__();
#endif
	}
}

int ccs::terminate(int excode)
{
	::exit(excode);
}

/////////////////////
}	// namespace tcsf

///////////////////
int main(int argc, char* argv[])
{
	int r = 0;
	tcsf::ccs cso;
	
	try {
		cso.initialize(argc, argv);
		
		cso.run();
		
	} catch(std::runtime_error& e) {
		std::cerr << e.what() << endl;
		r = cso.terminate(1);
	} catch(std::exception& e) {
		std::cerr << e.what() << endl;
		r = cso.terminate(1);
	} catch(...) {
		std::cerr << "unknown exception" << endl;
		r = cso.terminate(1);
	}
	
	return r;
}

